;; ugen-graph.scm - (c) rohan drape, 2003-2005

;; Construct a new UGen graph by applying the one argument <procedure>
;; p to each 'element' at the <ugen> `u'.  This *includes* u.  The
;; procedure is applied to an element *after* any sub-graph rooted at
;; that input is transformed.

(define (ugen-graph-transform u p)
  (p (cond ((ugen? u)
	    (ugen/inputs u (map (lambda (i) (ugen-graph-transform i p))
				(ugen-inputs u))))
	   ((output-proxy? u)
	    (make-output-proxy 
	     (ugen-graph-transform (output-proxy-ugen u) p)
	     (output-proxy-port u)))
	   ((control? u)
	    (control/proxy 
	     u 
	     (ugen-graph-transform (control-proxy u) p)))
	   ((constant? u)
	    u)
	   (else
	    (error "ugen-graph-transform: illegal input" u)))))

;; Return the <list> of all elements of the UGen graph rooted at `u'.
;; Elements will be of type <ugen>, <output-proxy>, <control> or
;; <constant>.

(define (ugen-graph-elements u)
  (cond 
   ((ugen? u) 
    (cons u (append-map ugen-graph-elements (ugen-inputs u))))
   ((output-proxy? u)
    (cons u (ugen-graph-elements (output-proxy-ugen u))))
   ((control? u)
    (cons u (ugen-graph-elements (control-proxy u))))
   ((constant? u)
    (list u))
   (else
    (error "ugen-graph-elements: illegal value" u))))

;; Return the <list> of all <constant> values that are referenced in
;; the UGen graph rooted at `u' in order and w/o duplicates.

(define (ugen-graph-constant-values u)
  (delete-duplicates 
   (map constant-value 
	(filter constant? (ugen-graph-elements u)))
   equal?))

;; Return the <list> of all <control> values that are referenced in
;; the UGen graph rooted at `u'.  The list is sorted by the <control>
;; index.

(define (ugen-graph-control-set u)
  (sort 
   (filter control? (ugen-graph-elements u))
   (lambda (a b) (< (control-index a) (control-index b)))))

;; Return the <list>, terminating with `u', and having the antecedents
;; of `u' in depth first order without duplication of shared UGens.

(define (ugen-graph-ordering u)
  (delete-duplicates
   (reverse
    (cons u (filter 
	     ugen? 
	     (map (lambda (u) 
		    (if (output-proxy? u) (output-proxy-ugen u) u))
		  (ugen-graph-elements u)))))
   equal?))

;; Return a <procedure> of one argument that will rewrite an input
;; <constant> with to have the correct index given the UGen graph
;; rooted at the <ugen> `u'.  *NOTE* It is important that equal? is
;; used as the comparison procedure here since member is used to
;; determine the value set.  In particular note that: (= 1 1.0) is
;; true and (equal? 1 1.0) is false.

(define (make-constant/close u)
  (let ((cc (ugen-graph-constant-values u)))
    (lambda (i)
      (constant/index 
       i (index (cut equal? <> (constant-value i)) cc)))))

;; Return a <procedure> of one argument that will rewrite an input
;; <ugen> value to have the correct index given the UGen graph rooted
;; at the <ugen> `u'.  *NOTE* It is important that ugen-eq? is used as
;; the comparison procedure here.

(define (make-ugen/close u)
  (let ((uu (ugen-graph-ordering u)))
    (lambda (i)
      (ugen/index i (index (cut ugen-eq? <> i) uu)))))

;; This is subtle.  The <ugen> value `u' is the root of a UGen graph.
;; The graph is assumed to be 'open', that is the values are not
;; 'closed'.  That is <constant> and <ugen> values do not have an
;; index.  Only a 'closed' UGen graph can be transformed into a
;; <graphdef> value.  

(define (ugen-graph-close u)
  (ugen-graph-transform 
   u (compose (mkT (make-ugen/close u) ugen?)
	      (mkT (make-constant/close u) constant?))))

(define (ugen-graph->graphdef name u)
  (let ((u* (ugen-graph-close u)))
    (make-graphdef 
     name
     (ugen-graph-constant-values u*)
     (map control-value (ugen-graph-control-set u*))
     (ugen-graph-control-set u*)
     (map ugen-rewrite/input (ugen-graph-ordering u*)))))

